// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// Package textproto实现对基于文本的请求/响应的通用支持
// HTTP、NNTP和SMTP样式的协议。
// 
// 该包提供：
// 
// Error，表示来自
// 服务器的数字错误响应。
// 
// 管道，用于管理客户端中的管道化请求和响应
// 。
// 
// 读卡器，要读取数字响应代码行，
// 键：值头、在续行上用前导空格包装的行
// 以及以
// 结尾的整文本块，其本身在一行上有一个点。
// 
// 编写器，用于编写点编码文本块。
// 
// Conn，一种方便的读写器和管道包装，可用于单个网络连接。
// 
package textproto

import (
	"bufio"
	"fmt"
	"io"
	"net"
)

// 错误表示来自服务器的数字错误响应。
type Error struct {
	Code int
	Msg  string
}

func (e *Error) Error() string {
	return fmt.Sprintf("%03d %s", e.Code, e.Msg)
}

// 协议错误将违反协议（如
// ）描述为无效响应或挂起连接。
type ProtocolError string

func (p ProtocolError) Error() string {
	return string(p)
}

// Conn表示文本网络协议连接。
// 它由一个读写器和一个管道组成，用于管理I/O 
// 和一个管道，用于对连接上的并发请求进行排序。
// 这些嵌入类型携带方法；
// 有关详细信息，请参阅这些类型的文档。
type Conn struct {
	Reader
	Writer
	Pipeline
	conn io.ReadWriteCloser
}

// NewConn使用Conn进行I/O返回一个新Conn。
func NewConn(conn io.ReadWriteCloser) *Conn {
	return &Conn{
		Reader: Reader{R: bufio.NewReader(conn)},
		Writer: Writer{W: bufio.NewWriter(conn)},
		conn:   conn,
	}
}

// 关闭连接。
func (c *Conn) Close() error {
	return c.conn.Close()
}

// 拨号使用网络连接到给定网络上的给定地址。拨号
// 然后返回连接的新连接。
func Dial(network, addr string) (*Conn, error) {
	c, err := net.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	return NewConn(c), nil
}

// Cmd是一种方便的方法，它在
// 在管道中等待轮到它之后发送命令。命令文本是
// 用args和appending格式化格式的结果\r\n。
// Cmd返回命令的id，用于StartResponse和EndResponse。
// 
// 例如，客户机可能会运行一个HELP命令，返回点体
// 使用：
// 
// id，err:=c.Cmd（“HELP”）
// if err！=nil{
// 返回nil，err 
// }
// 
// c.StartResponse（id）
// 延迟c.EndResponse（id）
// 
// 如果u，u，err=c.ReadCodeLine（110）；呃！=nil{
// 返回nil，err 
// }
// text，err:=c.ReadDotBytes（）
// 如果错误！=nil{
// 返回nil，err 
// }
// 返回c.ReadCodeLine（250）
// 
func (c *Conn) Cmd(format string, args ...interface{}) (id uint, err error) {
	id = c.Next()
	c.StartRequest(id)
	err = c.PrintfLine(format, args...)
	c.EndRequest(id)
	if err != nil {
		return 0, err
	}
	return id, nil
}

// TrimString返回不带前导和尾随ASCII空格的s。
func TrimString(s string) string {
	for len(s) > 0 && isASCIISpace(s[0]) {
		s = s[1:]
	}
	for len(s) > 0 && isASCIISpace(s[len(s)-1]) {
		s = s[:len(s)-1]
	}
	return s
}

// TrimBytes返回b，不带前导和尾随ASCII空格。
func TrimBytes(b []byte) []byte {
	for len(b) > 0 && isASCIISpace(b[0]) {
		b = b[1:]
	}
	for len(b) > 0 && isASCIISpace(b[len(b)-1]) {
		b = b[:len(b)-1]
	}
	return b
}

func isASCIISpace(b byte) bool {
	return b == ' ' || b == '\t' || b == '\n' || b == '\r'
}

func isASCIILetter(b byte) bool {
	b |= 0x20 // 使用小写字母
	return 'a' <= b && b <= 'z'
}
